fix(runner): always emit MESSAGES_SNAPSHOT to prevent compaction failures#1693
Conversation
…ures Three fixes to ensure MESSAGES_SNAPSHOT is always emitted, which is required for compactFinishedRun to succeed. Without it, sessions are marked corrupted and chat history is lost after the JSONL tail-read optimization (ffe4a21). - adapter.py: Remove `if run_messages:` guard around MESSAGES_SNAPSHOT emission. Runs that produce no assistant output (interrupted, halted on frontend tool, state-tool-only) still need a snapshot so compaction can succeed. When run_messages is empty the snapshot is the stamped input history, which is sufficient for compaction to find MESSAGES_SNAPSHOT and atomically replace the JSONL. - grpc_transport.py: Skip empty-payload gRPC messages instead of building a message with empty content. An empty-content message causes process_messages to return "" which triggers an early RunFinishedEvent before _stream_claude_sdk runs, so run_messages stays empty and MESSAGES_SNAPSHOT is never emitted. Affects both Operator-managed and control-plane-reconciled runner sessions. - run.py: Assign UUIDs to message dicts that lack an id in to_run_agent_input. Without ids, upsert_message falls back to append for every message, creating duplicates in the MESSAGES_SNAPSHOT for multi-turn sessions going through the gRPC dict message path. Co-Authored-By: Claude <noreply@anthropic.com>
✅ Deploy Preview for cheerful-kitten-f556a0 canceled.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThree defensive fixes in the ambient runner message pipeline: incoming messages are normalized with injected UUIDs when ChangesAmbient Runner Message Pipeline Robustness
🚥 Pre-merge checks | ✅ 7 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (7 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
Merge Queue Status
This pull request spent 12 seconds in the queue, including 2 seconds running CI. Required conditions to merge |
Summary
C1 —
grpc_transport.py: Skip gRPC messages with empty payloads instead of building an empty-content message. An empty-content message causesprocess_messagesto return"", which triggers an earlyRunFinishedEventbefore_stream_claude_sdkever runs — sorun_messagesstays empty andMESSAGES_SNAPSHOTis never emitted. Affects both Operator-managed and control-plane-reconciled runner sessions.C2 —
run.py: Assign UUIDs to message dicts missing anidinto_run_agent_input. Without ids,upsert_messagefalls back to append on every call, creating duplicates in the snapshot for multi-turn sessions going through the gRPC dict message path.C3 —
adapter.py: Remove theif run_messages:guard aroundMESSAGES_SNAPSHOTemission. Runs that produce no assistant output (interrupted, halted on frontend tool, state-management-tool-only turns) still need a snapshot socompactFinishedRuncan succeed. Whenrun_messagesis empty the snapshot contains the stamped input history, which is sufficient. Without a snapshot, compaction logs "session corrupted, keeping raw events" and chat history is lost after the tail-read optimization (ffe4a21).Root cause
compactFinishedRun(backendagui_store.go) requiresMESSAGES_SNAPSHOTto be present in the JSONL to perform compaction. If absent, it aborts and leaves raw streaming delta events in the file. On the next large-session tail-read (8e3bac3), those deltas may be missing from the head scan and the user sees blank chat history on reconnect.The
MESSAGES_SNAPSHOTgap was introduced whenambient-control-planeadded a second reconciled runner path: control-plane sessions reach the runner via gRPC (GRPCSessionListener) rather than HTTP, and the gRPC path has different message-shape guarantees that exposed all three gaps simultaneously.Test plan
MESSAGES_SNAPSHOTlog line should appear for everyRUN_FINISHED/RUN_ERRORturncompactionlog should showX raw events → Y snapshot events(not "session corrupted")🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes